
/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 *版权所有2002-2022原作者。
 *
 *根据Apache许可证2.0版（“许可证”）获得许可；
 *除非符合许可证，否则不得使用此文件。
 *您可以在以下地址获取许可证副本：
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *除非适用法律要求或书面同意，软件
 *根据许可证分发的内容是按“原样”分发的，
 *无任何明示或暗示的保证或条件。
 *有关管理权限和
 *许可证下的限制。
 */

package org.springframework.beans.factory.support;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessor;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.lang.Nullable;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;

/**
 * Bean definition reader for a simple properties format.
 *
 * <p>Provides bean definition registration methods for Map/Properties and
 * ResourceBundle. Typically applied to a DefaultListableBeanFactory.
 *
 * <p><b>Example:</b>
 *
 * <pre class="code">
 * employee.(class)=MyClass       // bean is of class MyClass
 * employee.(abstract)=true       // this bean can't be instantiated directly
 * employee.group=Insurance       // real property
 * employee.usesDialUp=false      // real property (potentially overridden)
 *
 * salesrep.(parent)=employee     // derives from "employee" bean definition
 * salesrep.(lazy-init)=true      // lazily initialize this singleton bean
 * salesrep.manager(ref)=tony     // reference to another bean
 * salesrep.department=Sales      // real property
 *
 * techie.(parent)=employee       // derives from "employee" bean definition
 * techie.(scope)=prototype       // bean is a prototype (not a shared instance)
 * techie.manager(ref)=jeff       // reference to another bean
 * techie.department=Engineering  // real property
 * techie.usesDialUp=true         // real property (overriding parent value)
 *
 * ceo.$0(ref)=secretary          // inject 'secretary' bean as 0th constructor arg
 * ceo.$1=1000000                 // inject value '1000000' at 1st constructor arg
 * </pre>
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 26.11.2003
 * @see DefaultListableBeanFactory
 * @deprecated as of 5.3, in favor of Spring's common bean definition formats
 * and/or custom reader implementations
 */
/**
 *简单财产格式的Bean定义读取器。
 *
 *<p>为Map/财产和
 *资源捆绑包。通常应用于DefaultListableBeanFactory。
 *
 *＜p＞＜b＞示例：</b>
 *
 *<pre-class=“code”>
 *员工。（class）=MyClass//bean属于MyClass类
 *员工。（abstract）=true//无法直接实例化此bean
 *employee.group=保险//不动产
 *employee.usesDialUp=false//真实属性（可能被重写）
 *
 *销售代表。（parent）=employee//源自“employee”bean定义
 *销售代表。（lazyinit）=true//惰性地初始化这个单例bean
 *salesrep.manager（ref）=tony//引用另一个bean
 *salesrep.department=销售//不动产
 *
 *技术人员。（parent）=employee//源自“employee”bean定义
 *技术人员。（scope）=prototype//bean是一个原型（不是共享实例）
 *technie.manager（ref）=jeff//引用另一个bean
 *technie.department=工程//不动产
 *technie.usesDialUp=true//真实属性（重写父值）
 *
 *首席执行官$0（ref）=秘书//将“秘书”bean注入为第0个构造函数参数
 *首席执行官$1=1000000//在第一个构造函数参数处注入值“1000000”
 *</pre>
 *
 *@作者Rod Johnson
 *@作者Juergen Hoeller
 *@作者Rob Harrop
 *@自2003年11月26日
 *@参见DefaultListableBeanFactory
 *@从5.3开始就被弃用，支持Spring的通用bean定义格式
 *和/或自定义读取器实现
 */
@Deprecated
public class PropertiesBeanDefinitionReader extends AbstractBeanDefinitionReader {

	/**
	 * Value of a T/F attribute that represents true.
	 * Anything else represents false. Case seNsItive.
	 */
	/**
	 *表示true的T/F属性的值。
	 *其他的都是假的。案例本身。
	 */
	public static final String TRUE_VALUE = "true";

	/**
	 * Separator between bean name and property name.
	 * We follow normal Java conventions.
	 */
	/**
	 *bean名称和属性名称之间的分隔符。
	 *我们遵循正常的Java约定。
	 */
	public static final String SEPARATOR = ".";

	/**
	 * Special key to distinguish {@code owner.(class)=com.myapp.MyClass}.
	 */
	/**
	 *用于区分｛@code owner.（class）=com.myapp.MyClass｝的特殊键。
	 */
	public static final String CLASS_KEY = "(class)";

	/**
	 * Special key to distinguish {@code owner.(parent)=parentBeanName}.
	 */
	/**
	 *用于区分｛@code owner.（parent）=parentBeanName｝的特殊键。
	 */
	public static final String PARENT_KEY = "(parent)";

	/**
	 * Special key to distinguish {@code owner.(scope)=prototype}.
	 * Default is "true".
	 */
	/**
	 *用于区分｛@code owner.（scope）=prototype｝的特殊键。
	 *默认值为“true”。
	 */
	public static final String SCOPE_KEY = "(scope)";

	/**
	 * Special key to distinguish {@code owner.(singleton)=false}.
	 * Default is "true".
	 */
	/**
	 *用于区分｛@code owner.（singleton）=false｝的特殊键。
	 *默认值为“true”。
	 */
	public static final String SINGLETON_KEY = "(singleton)";

	/**
	 * Special key to distinguish {@code owner.(abstract)=true}
	 * Default is "false".
	 */
	/**
	 *用于区分｛@code owner.（abstract）=true｝的特殊键
	 *默认值为“false”。
	 */
	public static final String ABSTRACT_KEY = "(abstract)";

	/**
	 * Special key to distinguish {@code owner.(lazy-init)=true}
	 * Default is "false".
	 */
	/**
	 *用于区分｛@code owner.（lazy init）=true｝的特殊键
	 *默认值为“false”。
	 */
	public static final String LAZY_INIT_KEY = "(lazy-init)";

	/**
	 * Property suffix for references to other beans in the current
	 * BeanFactory: e.g. {@code owner.dog(ref)=fido}.
	 * Whether this is a reference to a singleton or a prototype
	 * will depend on the definition of the target bean.
	 */
	/**
	 *当前中对其他bean的引用的属性后缀
	 *BeanFactory：例如｛@code owner.dog（ref）=fido｝。
	 *这是对单例还是原型的引用
	 *将取决于目标bean的定义。
	 */
	public static final String REF_SUFFIX = "(ref)";

	/**
	 * Prefix before values referencing other beans.
	 */
	/**
	 *引用其他bean的值之前的前缀。
	 */
	public static final String REF_PREFIX = "*";

	/**
	 * Prefix used to denote a constructor argument definition.
	 */
	/**
	 *用于表示构造函数参数定义的前缀。
	 */
	public static final String CONSTRUCTOR_ARG_PREFIX = "$";


	@Nullable
	private String defaultParentBean;

	private PropertiesPersister propertiesPersister = DefaultPropertiesPersister.INSTANCE;


	/**
	 * Create new PropertiesBeanDefinitionReader for the given bean factory.
	 * @param registry the BeanFactory to load bean definitions into,
	 * in the form of a BeanDefinitionRegistry
	 */
	/**
	 *为给定的bean工厂创建新的PropertiesBeanDefinitionReader。
	 *@param注册要加载bean定义的BeanFactory，
	 *以BeanDefinitionRegistry的形式
	 */
	public PropertiesBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}


	/**
	 * Set the default parent bean for this bean factory.
	 * If a child bean definition handled by this factory provides neither
	 * a parent nor a class attribute, this default value gets used.
	 * <p>Can be used e.g. for view definition files, to define a parent
	 * with a default view class and common attributes for all views.
	 * View definitions that define their own parent or carry their own
	 * class can still override this.
	 * <p>Strictly speaking, the rule that a default parent setting does
	 * not apply to a bean definition that carries a class is there for
	 * backwards compatibility reasons. It still matches the typical use case.
	 */
	/**
	 *为这个bean工厂设置默认的父bean。
	 *如果此工厂处理的子bean定义既不提供
	 *既不是父属性，也不是类属性，则使用此默认值。
	 *＜p＞可用于例如视图定义文件，以定义父级
	 *具有默认视图类和所有视图的公共属性。
	 *查看定义自己的父级或携带自己的父项的定义
	 *类仍然可以覆盖此。
	 *＜p＞严格来说，默认父设置的规则
	 *不适用于带有类的bean定义
	 *向后兼容的原因。它仍然符合典型的用例。
	 */
	public void setDefaultParentBean(@Nullable String defaultParentBean) {
		this.defaultParentBean = defaultParentBean;
	}

	/**
	 * Return the default parent bean for this bean factory.
	 */
	/**
	 *返回此bean工厂的默认父bean。
	 */
	@Nullable
	public String getDefaultParentBean() {
		return this.defaultParentBean;
	}

	/**
	 * Set the PropertiesPersister to use for parsing properties files.
	 * The default is {@code DefaultPropertiesPersister}.
	 * @see DefaultPropertiesPersister#INSTANCE
	 */
	/**
	 *设置用于分析财产文件的PropertiesPersister。
	 *默认值为｛@code DefaultPropertiesPersister｝。
	 *@参见DefaultPropertiesPersister#INSTANCE
	 */
	public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) {
		this.propertiesPersister =
				(propertiesPersister != null ? propertiesPersister : DefaultPropertiesPersister.INSTANCE);
	}

	/**
	 * Return the PropertiesPersister to use for parsing properties files.
	 */
	/**
	 *返回用于分析财产文件的PropertiesPersister。
	 */
	public PropertiesPersister getPropertiesPersister() {
		return this.propertiesPersister;
	}


	/**
	 * Load bean definitions from the specified properties file,
	 * using all property keys (i.e. not filtering by prefix).
	 * @param resource the resource descriptor for the properties file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #loadBeanDefinitions(org.springframework.core.io.Resource, String)
	 */
	/**
	 *从指定的财产文件加载bean定义，
	 *使用所有属性键（即不按前缀过滤）。
	 *@param resource财产文件的资源描述符
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeanDefinitionStoreException
	 *@参见#loadBeanDefinitions（org.springframework.core.io.Resource，字符串）
	 */
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource), null);
	}

	/**
	 * Load bean definitions from the specified properties file.
	 * @param resource the resource descriptor for the properties file
	 * @param prefix a filter within the keys in the map: e.g. 'beans.'
	 * (can be empty or {@code null})
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	/**
	 *从指定的财产文件加载bean定义。
	 *@param resource财产文件的资源描述符
	 *@param在映射中的键中添加筛选器前缀：例如“beans”
	 *（可以为空或｛@code null｝）
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeanDefinitionStoreException
	 */
	public int loadBeanDefinitions(Resource resource, @Nullable String prefix) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource), prefix);
	}

	/**
	 * Load bean definitions from the specified properties file.
	 * @param encodedResource the resource descriptor for the properties file,
	 * allowing to specify an encoding to use for parsing the file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	/**
	 *从指定的财产文件加载bean定义。
	 *@param encodedResource是财产文件的资源描述符，
	 *允许指定用于分析文件的编码
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeanDefinitionStoreException
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(encodedResource, null);
	}

	/**
	 * Load bean definitions from the specified properties file.
	 * @param encodedResource the resource descriptor for the properties file,
	 * allowing to specify an encoding to use for parsing the file
	 * @param prefix a filter within the keys in the map: e.g. 'beans.'
	 * (can be empty or {@code null})
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	/**
	 *从指定的财产文件加载bean定义。
	 *@param encodedResource是财产文件的资源描述符，
	 *允许指定用于分析文件的编码
	 *@param在映射中的键中添加筛选器前缀：例如“beans”
	 *（可以为空或｛@code null｝）
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeanDefinitionStoreException
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource, @Nullable String prefix)
			throws BeanDefinitionStoreException {

		if (logger.isTraceEnabled()) {
			logger.trace("Loading properties bean definitions from " + encodedResource);
		}

		Properties props = new Properties();
		try {
			try (InputStream is = encodedResource.getResource().getInputStream()) {
				if (encodedResource.getEncoding() != null) {
					getPropertiesPersister().load(props, new InputStreamReader(is, encodedResource.getEncoding()));
				}
				else {
					getPropertiesPersister().load(props, is);
				}
			}

			int count = registerBeanDefinitions(props, prefix, encodedResource.getResource().getDescription());
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + encodedResource);
			}
			return count;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("Could not parse properties from " + encodedResource.getResource(), ex);
		}
	}

	/**
	 * Register bean definitions contained in a resource bundle,
	 * using all property keys (i.e. not filtering by prefix).
	 * @param rb the ResourceBundle to load from
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #registerBeanDefinitions(java.util.ResourceBundle, String)
	 */
	/**
	 *注册资源包中包含的bean定义，
	 *使用所有属性键（即不按前缀过滤）。
	 *@param rb要从中加载的ResourceBundle
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeanDefinitionStoreException
	 *@参见#registerBeanDefinitions（java.util.ResourceBundle，字符串）
	 */
	public int registerBeanDefinitions(ResourceBundle rb) throws BeanDefinitionStoreException {
		return registerBeanDefinitions(rb, null);
	}

	/**
	 * Register bean definitions contained in a ResourceBundle.
	 * <p>Similar syntax as for a Map. This method is useful to enable
	 * standard Java internationalization support.
	 * @param rb the ResourceBundle to load from
	 * @param prefix a filter within the keys in the map: e.g. 'beans.'
	 * (can be empty or {@code null})
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	/**
	 *注册ResourceBundle中包含的bean定义。
	 *＜p＞类似于Map的语法。此方法用于启用
	 *标准Java国际化支持。
	 *@param rb要从中加载的ResourceBundle
	 *@param在映射中的键中添加筛选器前缀：例如“beans”
	 *（可以为空或｛@code null｝）
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeanDefinitionStoreException
	 */
	public int registerBeanDefinitions(ResourceBundle rb, @Nullable String prefix) throws BeanDefinitionStoreException {
		// Simply create a map and call overloaded method.
		// 只需创建一个映射并调用重载方法。
		Map<String, Object> map = new HashMap<>();
		Enumeration<String> keys = rb.getKeys();
		while (keys.hasMoreElements()) {
			String key = keys.nextElement();
			map.put(key, rb.getObject(key));
		}
		return registerBeanDefinitions(map, prefix);
	}


	/**
	 * Register bean definitions contained in a Map, using all property keys (i.e. not
	 * filtering by prefix).
	 * @param map a map of {@code name} to {@code property} (String or Object). Property
	 * values will be strings if coming from a Properties file etc. Property names
	 * (keys) <b>must</b> be Strings. Class keys must be Strings.
	 * @return the number of bean definitions found
	 * @throws BeansException in case of loading or parsing errors
	 * @see #registerBeanDefinitions(java.util.Map, String, String)
	 */
	/**
	 *使用所有属性键（即
	 *通过前缀过滤）。
	 *@param将｛@code name｝映射到｛@codeproperty｝（字符串或对象）。所有物
	 *如果来自财产文件等，则值为字符串。属性名称
	 *（keys）<b>必须</b>为字符串。类键必须是字符串。
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeansException
	 *@参见#registerBeanDefinitions（java.util.Map，String，String）
	 */
	public int registerBeanDefinitions(Map<?, ?> map) throws BeansException {
		return registerBeanDefinitions(map, null);
	}

	/**
	 * Register bean definitions contained in a Map.
	 * Ignore ineligible properties.
	 * @param map a map of {@code name} to {@code property} (String or Object). Property
	 * values will be strings if coming from a Properties file etc. Property names
	 * (keys) <b>must</b> be Strings. Class keys must be Strings.
	 * @param prefix a filter within the keys in the map: e.g. 'beans.'
	 * (can be empty or {@code null})
	 * @return the number of bean definitions found
	 * @throws BeansException in case of loading or parsing errors
	 */
	/**
	 *注册Map中包含的bean定义。
	 *忽略不合格的财产。
	 *@param将｛@code name｝映射到｛@codeproperty｝（字符串或对象）。所有物
	 *如果来自财产文件等，则值为字符串。属性名称
	 *（keys）<b>必须</b>为字符串。类键必须是字符串。
	 *@param在映射中的键中添加筛选器前缀：例如“beans”
	 *（可以为空或｛@code null｝）
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeansException
	 */
	public int registerBeanDefinitions(Map<?, ?> map, @Nullable String prefix) throws BeansException {
		return registerBeanDefinitions(map, prefix, "Map " + map);
	}

	/**
	 * Register bean definitions contained in a Map.
	 * Ignore ineligible properties.
	 * @param map a map of {@code name} to {@code property} (String or Object). Property
	 * values will be strings if coming from a Properties file etc. Property names
	 * (keys) <b>must</b> be Strings. Class keys must be Strings.
	 * @param prefix a filter within the keys in the map: e.g. 'beans.'
	 * (can be empty or {@code null})
	 * @param resourceDescription description of the resource that the
	 * Map came from (for logging purposes)
	 * @return the number of bean definitions found
	 * @throws BeansException in case of loading or parsing errors
	 * @see #registerBeanDefinitions(Map, String)
	 */
	/**
	 *注册Map中包含的bean定义。
	 *忽略不合格的财产。
	 *@param将｛@code name｝映射到｛@codeproperty｝（字符串或对象）。所有物
	 *如果来自财产文件等，则值为字符串。属性名称
	 *（keys）<b>必须</b>为字符串。类键必须是字符串。
	 *@param在映射中的键中添加筛选器前缀：例如“beans”
	 *（可以为空或｛@code null｝）
	 *@param resourceDescription描述
	 *地图来自（出于记录目的）
	 *@return找到的bean定义的数量
	 *在加载或分析错误时@throws BeansException
	 *@参见#registerBeanDefinitions（映射，字符串）
	 */
	public int registerBeanDefinitions(Map<?, ?> map, @Nullable String prefix, String resourceDescription)
			throws BeansException {

		if (prefix == null) {
			prefix = "";
		}
		int beanCount = 0;

		for (Object key : map.keySet()) {
			if (!(key instanceof String keyString)) {
				throw new IllegalArgumentException("Illegal key [" + key + "]: only Strings allowed");
			}
			if (keyString.startsWith(prefix)) {
				// Key is of form: prefix<name>.property
				// 密钥的格式为：prefix＜name＞.properties
				String nameAndProperty = keyString.substring(prefix.length());
				// Find dot before property name, ignoring dots in property keys.
				// 查找属性名称之前的点，忽略属性键中的点。
				int sepIdx ;
				int propKeyIdx = nameAndProperty.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX);
				if (propKeyIdx != -1) {
					sepIdx = nameAndProperty.lastIndexOf(SEPARATOR, propKeyIdx);
				}
				else {
					sepIdx = nameAndProperty.lastIndexOf(SEPARATOR);
				}
				if (sepIdx != -1) {
					String beanName = nameAndProperty.substring(0, sepIdx);
					if (logger.isTraceEnabled()) {
						logger.trace("Found bean name '" + beanName + "'");
					}
					if (!getRegistry().containsBeanDefinition(beanName)) {
						// If we haven't already registered it...
						// 如果我们还没有注册。。。
						registerBeanDefinition(beanName, map, prefix + beanName, resourceDescription);
						++beanCount;
					}
				}
				else {
					// Ignore it: It wasn't a valid bean name and property,
					// 忽略它：它不是有效的bean名称和属性，
					// although it did start with the required prefix.
					// 尽管它确实以所需前缀开头。
					if (logger.isDebugEnabled()) {
						logger.debug("Invalid bean name and property [" + nameAndProperty + "]");
					}
				}
			}
		}

		return beanCount;
	}

	/**
	 * Get all property values, given a prefix (which will be stripped)
	 * and add the bean they define to the factory with the given name.
	 * @param beanName name of the bean to define
	 * @param map a Map containing string pairs
	 * @param prefix prefix of each entry, which will be stripped
	 * @param resourceDescription description of the resource that the
	 * Map came from (for logging purposes)
	 * @throws BeansException if the bean definition could not be parsed or registered
	 */
	/**
	 *获取给定前缀的所有属性值（将去掉前缀）
	 *并将它们定义的bean添加到具有给定名称的工厂中。
	 *@param beanName要定义的bean的名称
	 *@param map包含字符串对的map
	 *@param每个条目的前缀，将被删除
	 *@param resourceDescription描述
	 *地图来自（出于记录目的）
	 *如果无法解析或注册bean定义，@throws BeansException
	 */
	protected void registerBeanDefinition(String beanName, Map<?, ?> map, String prefix, String resourceDescription)
			throws BeansException {

		String className = null;
		String parent = null;
		String scope = BeanDefinition.SCOPE_SINGLETON;
		boolean isAbstract = false;
		boolean lazyInit = false;

		ConstructorArgumentValues cas = new ConstructorArgumentValues();
		MutablePropertyValues pvs = new MutablePropertyValues();

		String prefixWithSep = prefix + SEPARATOR;
		int beginIndex = prefixWithSep.length();

		for (Map.Entry<?, ?> entry : map.entrySet()) {
			String key = ((String) entry.getKey()).strip();
			if (key.startsWith(prefixWithSep)) {
				String property = key.substring(beginIndex);
				if (CLASS_KEY.equals(property)) {
					className = ((String) entry.getValue()).strip();
				}
				else if (PARENT_KEY.equals(property)) {
					parent = ((String) entry.getValue()).strip();
				}
				else if (ABSTRACT_KEY.equals(property)) {
					String val = ((String) entry.getValue()).strip();
					isAbstract = TRUE_VALUE.equals(val);
				}
				else if (SCOPE_KEY.equals(property)) {
					// Spring 2.0 style
					// 弹簧2.0样式
					scope = ((String) entry.getValue()).strip();
				}
				else if (SINGLETON_KEY.equals(property)) {
					// Spring 1.2 style
					// 弹簧1.2样式
					String val = ((String) entry.getValue()).strip();
					scope = (!StringUtils.hasLength(val) || TRUE_VALUE.equals(val) ?
							BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
				}
				else if (LAZY_INIT_KEY.equals(property)) {
					String val = ((String) entry.getValue()).strip();
					lazyInit = TRUE_VALUE.equals(val);
				}
				else if (property.startsWith(CONSTRUCTOR_ARG_PREFIX)) {
					if (property.endsWith(REF_SUFFIX)) {
						int index = Integer.parseInt(property, 1, property.length() - REF_SUFFIX.length(), 10);
						cas.addIndexedArgumentValue(index, new RuntimeBeanReference(entry.getValue().toString()));
					}
					else {
						int index = Integer.parseInt(property, 1, property.length(), 10);
						cas.addIndexedArgumentValue(index, readValue(entry));
					}
				}
				else if (property.endsWith(REF_SUFFIX)) {
					// This isn't a real property, but a reference to another prototype
					// 这不是一个真实的属性，而是对另一个原型的引用
					// Extract property name: property is of form dog(ref)
					// 提取属性名称：属性的格式为dog（ref）
					property = property.substring(0, property.length() - REF_SUFFIX.length());
					String ref = ((String) entry.getValue()).strip();

					// It doesn't matter if the referenced bean hasn't yet been registered:
					// 如果引用的bean尚未注册，则无所谓：
					// this will ensure that the reference is resolved at runtime.
					// 这将确保在运行时解析引用。
					Object val = new RuntimeBeanReference(ref);
					pvs.add(property, val);
				}
				else {
					// It's a normal bean property.
					// 这是正常的bean属性。
					pvs.add(property, readValue(entry));
				}
			}
		}

		if (logger.isTraceEnabled()) {
			logger.trace("Registering bean definition for bean name '" + beanName + "' with " + pvs);
		}

		// Just use default parent if we're not dealing with the parent itself,
		// 如果我们不处理父对象本身，请使用默认父对象，
		// and if there's no class name specified. The latter has to happen for
		// 如果没有指定类名。后者必须发生在
		// backwards compatibility reasons.
		// 向后兼容的原因。
		if (parent == null && className == null && !beanName.equals(this.defaultParentBean)) {
			parent = this.defaultParentBean;
		}

		try {
			AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(
					parent, className, getBeanClassLoader());
			bd.setScope(scope);
			bd.setAbstract(isAbstract);
			bd.setLazyInit(lazyInit);
			bd.setConstructorArgumentValues(cas);
			bd.setPropertyValues(pvs);
			getRegistry().registerBeanDefinition(beanName, bd);
		}
		catch (ClassNotFoundException ex) {
			throw new CannotLoadBeanClassException(resourceDescription, beanName, className, ex);
		}
		catch (LinkageError err) {
			throw new CannotLoadBeanClassException(resourceDescription, beanName, className, err);
		}
	}

	/**
	 * Reads the value of the entry. Correctly interprets bean references for
	 * values that are prefixed with an asterisk.
	 */
	/**
	 *读取条目的值。正确解释的bean引用
	 *前缀为星号的值。
	 */
	private Object readValue(Map.Entry<?, ?> entry) {
		Object val = entry.getValue();
		if (val instanceof String strVal) {
			// If it starts with a reference prefix...
			// 如果它以引用前缀开头。。。
			if (strVal.startsWith(REF_PREFIX)) {
				// Expand the reference.
				// 展开参考。
				String targetName = strVal.substring(1);
				if (targetName.startsWith(REF_PREFIX)) {
					// Escaped prefix -> use plain value.
					// 转义前缀->使用纯值。
					val = targetName;
				}
				else {
					val = new RuntimeBeanReference(targetName);
				}
			}
		}
		return val;
	}

}
